﻿#region

using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using JsMapper.BO;
using JsMapper.BO.Attributes;
using JsMapper.Helper;

#endregion

namespace JsMapper.Mappers
{
    public class TypeScript : IJsModelMapper
    {
        public string GetJs(IJsModel model)
        {
            var props = GetTsProperties(model);
            var code = GetTsPropertyCode(props);

            return WrapCodeInModule(model.JsNamespace, model.Name, code);
        }

        private string GetTsType(PropertyInfo property, JsModelAttribute jsModel)
        {
            var tsNamespace = string.IsNullOrEmpty(jsModel.JsNamespace) ? string.Empty : jsModel.JsNamespace + ".";
            var propName = GetPropertyTypeName(property, jsModel);

            return tsNamespace + propName;
        }

        private string GetTsType(PropertyInfo property)
        {
            var jsType = property.PropertyType.GetJsType();

            if (jsType == "boolean") return "bool";
            if (jsType != "object") return jsType;

            return "any";
        }

        private string GetPropertyTypeName(PropertyInfo property, JsModelAttribute jsModel)
        {
            if (jsModel == null) return property.Name;
            return string.IsNullOrEmpty(jsModel.Name) ? property.PropertyType.Name : jsModel.Name;
        }

        private Dictionary<string, string> GetTsProperties(IJsModel model)
        {
            var tsProps = new Dictionary<string, string>();

            foreach (var property in model.Properties)
            {
                var jsModel = (JsModelAttribute)property.PropertyType.GetCustomAttribute(typeof(JsModelAttribute), true);
                var propType = jsModel != null ? GetTsType(property, jsModel) : GetTsType(property);

                tsProps.Add(property.Name, propType);
            }

            return tsProps;
        }

        private string GetTsPropertyCode(Dictionary<string, string> tsProperties)
        {
            return tsProperties.Aggregate(string.Empty, (current, property) => current + string.Format("{0}: {1};", property.Key, property.Value));
        }

        private string WrapCodeInModule(string jsNamespace, string interfaceName, string code)
        {
            var ret = string.Empty;
            var bracketsOnEnd = 0;
            if (jsNamespace == null) jsNamespace = string.Empty;

            if(jsNamespace.Contains('.'))
            {
                foreach (var ns in jsNamespace.Split('.'))
                {
                    ret += "export module " + ns + " {";
                    bracketsOnEnd++;
                }
            }

            ret += string.Format("export class {0} {{{1}}}", interfaceName, code);
            for (var i = 0; i < bracketsOnEnd; i++) ret += "}";

            return ret;
        }
    }
}